﻿/*
Typps HTML BaseControlDesigner for ASP.NET
 L|ypps Web controls for ASP.NET

Copyright (c) 2009 Alessandro Zifiglio, http://www.Typps.com
EMail: alessandro@Typps.com

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
namespace Typps
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.Configuration;
    using System.Drawing;
    using System.IO;
    using System.Text;
    using System.Web;
    using System.Web.Configuration;
    using System.Web.UI;
    using System.Web.UI.Design;
    using System.Web.UI.Design.WebControls;
    using System.Web.UI.WebControls;
    using System.Windows.Forms;
    using System.Xml;

    public class BaseControlDesigner : CompositeControlDesigner
    {
        #region Fields

        const string configurationSectionPath = "configuration";
        const string handlerName = "WebResourceHandler";
        const string handlerType = "Typps.WebResourceHandler";
        const string iis6HandlerSectionPath = "configuration/system.web/httpHandlers";
        const string iis7HandlerSectionPath = "configuration/system.webServer/handlers";
        const string noConfigurationSection = "Your web.config is empty. You need to atleast define a root <configuration> section. Then drag and drop this control again on your webform in design view.";
        const string systemWebSectionPath = "configuration/system.web"; //when registering iis6 handlers
        const string systemWebServerSectionPath = "configuration/system.webServer";
        const string webConfigMissing = "Your web.config is required because you need to add httphandlers. The control can add the handlers for you if you add a valid web.config file that at a minimum has a root <configuration> element.";

        #endregion Fields

        #region Properties

        public override DesignerVerbCollection Verbs
        {
            get
            {
                DesignerVerbCollection verbsValue = new DesignerVerbCollection();
                verbsValue.Add(new DesignerVerb("Retrieve Autogenerated Css", new System.EventHandler(OnRetrieveAutoGeneratedCss)));

                return verbsValue;
            }
        }

        #endregion Properties

        #region Methods

        public override string GetDesignTimeHtml()
        {
            SetupHandlers();
            return base.GetDesignTimeHtml();
        }

        protected virtual void OnRetrieveAutoGeneratedCss(object sender, EventArgs args)
        {
        }

        XmlNode CreateNewHttpHandlerSection(ConfigXmlDocument document, IISVersion version)
        {
            string systemSectionPath = systemWebSectionPath;
            if (version == IISVersion.IIS7)
                systemSectionPath = systemWebServerSectionPath;
            XmlNode systemSection = document.SelectSingleNode(systemSectionPath);
            if (systemSection == null)
            {
                XmlNode configurationSection = document.SelectSingleNode(configurationSectionPath);
                if (configurationSection == null)
                    throw new Exception(noConfigurationSection);
                string webSectionName = "system.web";//IIS6
                if (version == IISVersion.IIS7)
                    webSectionName = "system.webServer";
                systemSection = document.CreateNode(XmlNodeType.Element, webSectionName, null);
                configurationSection.AppendChild(systemSection);
            }

            string handlerSectionName = "httpHandlers";//IIS6
            if (version == IISVersion.IIS7)
                handlerSectionName = "handlers";

            XmlNode handlerSection = document.CreateNode(XmlNodeType.Element, handlerSectionName, null);

            systemSection.AppendChild(handlerSection);
            return handlerSection;
        }

        bool IsHandlerRegistered(XmlNode handlerNode)
        {
            if (handlerNode == null) return false;

            string attributeName = "type";
            foreach (XmlNode xn in handlerNode.ChildNodes)
            {
                if (xn.NodeType == XmlNodeType.Element)
                {
                    XmlAttribute xa = xn.Attributes[attributeName];
                    if (xa != null)
                    {
                        if (xa.Value.Equals(handlerType, StringComparison.OrdinalIgnoreCase))
                        {
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        void SaveConfigDoc(ConfigXmlDocument document, string path)
        {
            XmlTextWriter writer = new XmlTextWriter(path, null);
            writer.Formatting = Formatting.Indented;
            document.WriteTo(writer);
            writer.Flush();
            writer.Close();
        }

        void SetHttpHandlersSection(string path, IISVersion version)
        {
            if (string.IsNullOrEmpty(path)) return;
            bool isRegistered = false;

            string sectionPath = (version == IISVersion.IIS6) ?
            iis6HandlerSectionPath : iis7HandlerSectionPath;
            //use a streamreader instead of loading the file directly in
            //ConfigXmlDocument, otherwise it simply locks the file
            //and since it has no disposer, your literally stuck
            //waiting for it to liberate the file :|
            ConfigXmlDocument document = null;
            using (StreamReader sr = new StreamReader(path))
            {
                if (sr == null)
                    throw new Exception(webConfigMissing);

                document = new ConfigXmlDocument();
                document.Load(sr);
                XmlNode handlerNode = document.SelectSingleNode(sectionPath);
                if (handlerNode == null)
                    handlerNode = CreateNewHttpHandlerSection(document, version);

                isRegistered = IsHandlerRegistered(handlerNode);

                if (!isRegistered)
                {
                    XmlNode newHandlerNode = document.CreateNode(XmlNodeType.Element, "add", null);
                    XmlAttribute verb = document.CreateAttribute("verb");
                    verb.Value = "*";
                    newHandlerNode.Attributes.Append(verb);

                    XmlAttribute pathAttribute = document.CreateAttribute("path");
                    pathAttribute.Value = "typps.axd";
                    newHandlerNode.Attributes.Append(pathAttribute);

                    XmlAttribute type = document.CreateAttribute("type");
                    type.Value = handlerType;
                    newHandlerNode.Attributes.Append(type);

                    if (version == IISVersion.IIS7)
                    {
                        XmlAttribute name = document.CreateAttribute("name");
                        name.Value = handlerName;
                        newHandlerNode.Attributes.Append(name);

                        XmlAttribute preCondition = document.CreateAttribute("preCondition");
                        preCondition.Value = "integratedMode";
                        newHandlerNode.Attributes.Append(preCondition);
                    }
                    handlerNode.AppendChild(newHandlerNode);
                }
            }
            if (!isRegistered)
                SaveConfigDoc(document, path);
        }

        void SetupHandlers()
        {
            try
            {
                IWebApplication webApplication =
                (IWebApplication)this.GetService(typeof(IWebApplication));

                if (webApplication != null)
                {
                    IProjectItem projectItem = webApplication.RootProjectItem;
                    string path = projectItem.PhysicalPath + "web.config";

                    bool skip = SkipRegistration(path);
                    if (skip) return;

                    SetHttpHandlersSection(path, IISVersion.IIS7);
                    SetHttpHandlersSection(path, IISVersion.IIS6);
                }
            }
            catch (Exception e) { throw e; }
        }

        bool SkipRegistration(string path)
        {
            /*simply checks if either one of the handlers is registered, if so the user deleted one manually and we don't want to override his settings, so skip*/
             using (StreamReader sr = new StreamReader(path))
             {
                 if (sr == null)
                     throw new Exception(webConfigMissing);

                 ConfigXmlDocument document = new ConfigXmlDocument();
                 document.Load(sr);

                 XmlNode handlerNode = document.SelectSingleNode(iis6HandlerSectionPath);
                 bool isRegistered = IsHandlerRegistered(handlerNode);
                 if (isRegistered)
                     return true;
                 handlerNode = document.SelectSingleNode(iis7HandlerSectionPath);
                 isRegistered = IsHandlerRegistered(handlerNode);
                 return isRegistered;
             }
        }

        #endregion Methods
    }
}